package com.gmrz.uaf.remote.protocol.v1.processor;

import com.gmrz.uaf.common.GuiceUtil;
import com.gmrz.uaf.common.UAFException;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.policy.verification.PolicyVerificationException;
import com.gmrz.uaf.policy.verification.StepupPolicyProcessor;
import com.gmrz.uaf.protocol.v1.processor.TransactionContentProcessor;
import com.gmrz.uaf.protocol.v1.processor.UAFAuthInitProcessor;
import com.gmrz.uaf.protocol.v1.processor.exception.*;
import com.gmrz.uaf.protocol.v1.schema.*;
import com.gmrz.uaf.remote.plugin.AuthServicePluginManager;
import com.gmrz.uas.plugin.authservice.AuthServiceData;
import com.gmrz.uas.plugin.authservice.AuthServicePlugin;
import com.gmrz.uas.plugin.exception.PluginException;
import com.gmrz.util.Convert;
import com.gmrz.util.CryUtil;
import com.google.inject.Inject;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Created by gongyuqiang on 2017/5/15.
 */
public class UAPAuthInitProcessor extends UAFAuthInitProcessor {
    private static final Logger LOG = LogManager.getLogger(UAPAuthInitProcessor.class);

    AuthServicePluginManager serviceManager;

    @Inject
    public void setServiceManager(AuthServicePluginManager serviceManager) {
        this.serviceManager = serviceManager;
    }

    /**
     * 远程认证request
     * @param custNo
     * @param hashedUserName
     * @param appID
     * @param deviceID
     * @param authType
     * @param transType
     * @param transactionText
     * @return
     * @throws PolicyNotFoundException
     * @throws TxnContentCreationException
     * @throws DAOException
     * @throws AuthenticatorNotRegisteredException
     * @throws PolicyVerificationException
     */
    @Override
    public List<AuthenticationRequest> request(String custNo, String hashedUserName, String appID, String deviceID, List<String> authType,
                                               String transType, String transactionText,byte[] challengeParam,String ext)
            throws PolicyNotFoundException, TxnContentCreationException, DAOException,
            AuthenticatorNotRegisteredException,
            PolicyVerificationException {

        AuthenticationRequest request = GuiceUtil.getProcessorInjector().getInstance(AuthenticationRequest.class);

        byte[] challenge = getRandom(32);
        challenge = ArrayUtils.addAll(challenge,challengeParam);
        Policy p = null;
        Set<Authenticator> registeredList = null;
        String t_authType = null;
        for(String auth:authType){
            p = this.policyManager.getNamedPolicy(auth,appID,transType);
            if (p == null) {
                continue;
            }
            registeredList = getAuthenticatorListForAlreadyRegisteredNoDeviceId(hashedUserName,appID,
                    auth,transType);

            if ((registeredList == null) || (registeredList.isEmpty())) {
                continue;
            }

            t_authType = auth;
        }
        if (p == null) {
            LOG.error("Requested policy {} is not available on server", "POLICY_"+appID+"_"+authType);
            throw new PolicyNotFoundException(UAFErrorCode.BIZ_POLICY_NOT_FOUND);
        }

        if ((registeredList == null) || (registeredList.isEmpty())) {
            throw new AuthenticatorNotRegisteredException(UAFErrorCode.BIZ_AUTHENTICATOR_NOT_FOUND);
        }
        StepupPolicyProcessor.filterPolicy(p, registeredList);


        MatchCriteria acceptedMatchCriteria = getAcceptedMatchCriteria(p);
        if(acceptedMatchCriteria != null){
            String pluginUserName = CryUtil.getSHA(hashedUserName + "_" + appID + "_" + t_authType + "_" + transType);
            LOG.debug("pluginusername=" + hashedUserName + "_" + appID + "_" + t_authType + "_" + transType);
            initAuthenticatorExts(acceptedMatchCriteria, t_authType, pluginUserName);
        }

        request.getOperationHeader().getServerData().withChallenge(challenge).withPolicyName(p.getPolicyName())
                .withUserName(custNo);
        request.getOperationHeader().setApplicationID(this.getServerConfig().getApplicationID(appID,transType));
        request.setServerChallenge(new ServerChallenge().withChallenge(challenge));
        request.setPolicy(p);

        if (transactionText != null) {
            try {
                request.setTransactions(TransactionContentProcessor.create(hashedUserName, this.dbutil, transactionText,
                        request.getOperationHeader().getServerData(), this.cryptoEngine,appID,deviceID,t_authType,transType));
            } catch (Exception ex) {
                LOG.error("Failed to create the transaction from given transaction text[{}]",
                        transactionText, ex);
                if ((ex instanceof UAFException)) {
                    UAFException uafe = (UAFException) ex;
                    throw new TxnContentCreationException(uafe.getErrorCode());
                }
                throw new TxnContentCreationException(UAFErrorCode.PROTOCOL_TXN_CONTENT_CREATE_FAILED);
            }
        }
        //自服务处理appID
        if(serverConfig.getAppIdUrlBoolean(appID,transType)) {
            setFacetsAaid(request, appID);
        }
        List requests = new ArrayList();
        requests.add(request);
        return requests;
    }

    /**
     * 获取远程认证数据，例如：声纹数字
     * @param criteria
     * @param authType
     * @param username
     */
    public void initAuthenticatorExts(MatchCriteria criteria, String authType, String username) {
        try{
            String aaid = criteria.getAAID().get(0).toString();
            AuthServicePlugin service = serviceManager.getServicePlugin(aaid, authType);
            List<AuthServiceData> contents = service.preauthenticate(username);
            if(contents != null && contents.size() > 0){
                List<Extension> exts = new ArrayList<Extension>();
                for(AuthServiceData content: contents){
                    Extension ext = new Extension();
                    ext.setId(content.getId());
                    ext.setData(Convert.toBase64(content.getContent()));
                    exts.add(ext);
                }
                criteria.setExtensions(exts);
            }
        }catch (PluginException e){
            LOG.error(e.getErrorMsg());
            throw new AuthenticationFailedException(e.getErrorMsg());
        }

    }
}
